﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Caching;
using System.Web;
using QD.Data.Page;

namespace QD.Cache
{
    public class CacheListManager<T> where T : class,ICacheObject, new()
    {

        public readonly string KEY_LIST_FORMAT = null;
        public readonly string KEY_ITEM_FORMAT = null;
        IDictionary<String, IList<String>> cacheIndex = null;
        Object lockObj = null;
        public CacheListManager()
        {
            KEY_ITEM_FORMAT = "__jCache_" + typeof(T).FullName + ".{0}";
            KEY_LIST_FORMAT = "__jCache_" + typeof(T).FullName;
            key_map = new Dictionary<String, Int32>();
            lockObj = new Object();
        }


        public int CacheTimeMinute { get; set; }
        public delegate IList<T> ResolveHandler();
        public delegate T ResolveItemHandler(String key);
        public ResolveHandler Resolver = null;
        public ResolveItemHandler ItemResolver = null;

        public T this[string key]
        {
            get
            {
                T item = default(T);
                if (this.Items != null && key_map.ContainsKey(key))
                {
                    Int32 idx = key_map[key];
                    item = this.Items[idx];
                }
                else
                {
                    if (ItemResolver != null)
                        item = ItemResolver(key);
                    if (item != null)
                        this.Append(item);
                }
                return item;
            }
        }

        public T this[Int32 key] { get { return this[key.ToString()]; } }

        public T this[Int64 key] { get { return this[key.ToString()]; } }

        private IDictionary<String, Int32> key_map = null;

        protected void Add(IList<T> list)
        {

            String key = null;
            for (int i = 0; i < list.Count; i++)
            {
                key = list[i].__get_cache_key().ToString();
                key_map.Add(key, i);
            }
            DateTime expireTime = DateTime.Now.AddMinutes(CacheTimeMinute);
            CacheDependency dependency = null;
            HttpRuntime.Cache.Insert(
                KEY_LIST_FORMAT, list,
                dependency, expireTime,
                System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal,
                delegate(String str, object obj, CacheItemRemovedReason reason)
                {
                    key_map.Clear();
                });
        }

        public void Append(T item)
        {
            lock (lockObj)
            {
                String key = item.__get_cache_key().ToString();
                if (!this.key_map.ContainsKey(key))
                {
                    this.Items.Add(item);
                    if (!this.key_map.ContainsKey(key))
                        this.key_map.Add(key, this.Items.Count - 1);
                }
            }
        }

        public void Replace(String key)
        {
            lock (lockObj)
            {
                if (key_map.ContainsKey(key))
                {
                    Int32 idx = key_map[key];
                    if (ItemResolver != null)
                        this.Items[idx] = ItemResolver(key);
                }
            }
        }

        public void Replace(Int32 key) { Replace(key.ToString()); }
        public void Replace(Int64 key) { Replace(key.ToString()); }

        public void Replace(T item)
        {
            lock (lockObj)
            {
                String key = item.__get_cache_key().ToString();
                if (key_map.ContainsKey(key))
                {
                    Int32 idx = key_map[key];
                    this.Items[idx] = item;
                }
            }

        }


        public void Append(Int32 key)
        {
            if (ItemResolver != null)
            {
                T item = ItemResolver(key.ToString());
                this.Append(item);
            }
        }

        public IList<T> Find(Int32 top)
        {
            List<T> list = new List<T>(Math.Min(top, this.Items.Count));
            for (int i = 0; i < this.Items.Count && list.Count < top; i++)
                list.Add(this.Items[i]);
            return list;
        }

        public PageList<T> Find(Int32 page, Int32 pagesize, Comparison<T> comparer = null)
        {
            List<T> list = new List<T>(Math.Min(pagesize, this.Items.Count));
            Int32 i = 0;

            List<T> list_src = this.Items as List<T>;
            if (comparer != null)
            {
                list_src = new List<T>(this.Items);
                list_src.Sort(comparer);
            }
            foreach (T item in list_src)
            {
                if (i++ < (page - 1) * pagesize) continue;
                list.Add(item);
                if (list.Count >= pagesize) break;
            }
            return new PageList<T>(list, this.Items.Count);
        }

        public PageList<T> Find(Int32 page, Int32 pagesize, Predicate<T> match, Comparison<T> comparer = null)
        {
            List<T> list = Find(match) as List<T>;
            if (list == null || list.Count == 0) return PageList<T>.Empty;
            Int32 start = 0, count = 0;
            start = (page - 1) * pagesize;
            count = Math.Min(list.Count - start, pagesize);
            if (comparer != null)
                list.Sort(comparer);
            IList<T> _list = list.GetRange(start, count);
            return new PageList<T>(_list, list.Count);
        }

        public IList<T> Find(Int32 top, Predicate<T> match)
        {
            Int32 num = Math.Min(top, this.Items.Count);
            IList<T> _list = Find(match);
            if (_list == null || _list.Count == 0) return null;
            List<T> list = _list as List<T>;

            return list.GetRange(0, Math.Min(list.Count, top));
        }

        public IList<T> Find(Predicate<T> match, Comparison<T> comparer = null)
        {
            List<T> list = new List<T>(this.Items);
            if (comparer != null)
                list.Sort(comparer);
            return list.FindAll(match);
        }


        public IList<T> Rand(Int32 top)
        {
            Int32 num = Math.Min(top, this.Items.Count);
            List<T> list = new List<T>(num);
            List<T> _list = new List<T>(this.Items);
            Int32 idx = 0;
            Random r = new Random();
            while (num-- > 0)
            {
                idx = r.Next(_list.Count);
                list.Add(_list[idx]);
                _list.RemoveAt(idx);
            }
            return list;
        }

        public IList<T> Rand(Int32 top, Predicate<T> filter)
        {
            IList<T> _list = Find(filter);
            Int32 num = Math.Min(top, _list.Count);

            if (_list == null || _list.Count == 0) return null;
            List<T> list = new List<T>(num);
            Random r = new Random();
            Int32 idx = 0;
            while (num-- > 0)
            {
                idx = r.Next(_list.Count);
                list.Add(_list[idx]);
                _list.RemoveAt(idx);
            }
            return list;
        }



        public T Get(Predicate<T> filter)
        {
            foreach (T item in this.Items)
            {
                if (filter(item))
                    return item;
            }
            return null;
        }

        public IList<T> Items
        {
            get
            {
                IList<T> list = HttpRuntime.Cache.Get(KEY_LIST_FORMAT) as IList<T>;
                if (list == null)
                {
                    if ((list = Resolver()) != null)
                        Add(list);
                }
                return list;
            }
        }
        public void Clear()
        {
            HttpRuntime.Cache.Remove(KEY_LIST_FORMAT);
        }




    }
}
